package top.v5it.japi.plus.common.func;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * 方法模板，在方法调用不确定情况是否异常时使用
 *
 * @author zhanpu
 * @date 2022/4/22
 */
public abstract class MethodTemplate {

    /**
     * 不确定的方法调用是否异常情况可采用，出现异常返回给定默认值
     *
     * @param callback     方法调用函数
     * @param defaultValue 异常时返回的默认值
     * @param <T>          返回值类型
     * @param <E>          异常类型
     * @return 方法返回值
     */
    public static <T, E extends Throwable> T execute(Func0<T, ? extends E> callback, T defaultValue) {
        Objects.requireNonNull(callback);

        try {
            return Optional
                    .ofNullable(callback.get())
                    .orElse(defaultValue);
        } catch (Throwable e) {
            return defaultValue;
        }
    }

    /**
     * 不确定的方法调用是否异常情况可采用，出现异常返回自定义异常
     *
     * @param func        方法调用函数
     * @param defaultFunc 当异常时返回函数
     * @param <T>         返回值类型
     * @param <E>         方法调用函数可能抛出的异常类型
     * @param <X>         当异常时返回函数异常类型
     * @return 返回值
     */
    public static <T, E extends Throwable, X extends Throwable> T executeWithFunc0(Func0<T, ? extends E> func, Func0<T, ? extends X> defaultFunc) throws X {
        Objects.requireNonNull(func);
        Objects.requireNonNull(defaultFunc);

        try {
            return Objects.requireNonNull(func.get());
        } catch (Throwable e) {
            return defaultFunc.get();
        }
    }

    /**
     * 不确定的方法调用是否异常情况可采用，出现异常返回自定义异常
     *
     * @param func     方法调用函数
     * @param supplier 当异常时返回函数
     * @param <T>      返回值类型
     * @param <E>      方法调用函数可能抛出的异常类型
     * @param <X>      当异常时返回函数异常类型
     * @return 返回值
     */
    public static <T, E extends Throwable, X extends Throwable> T executeWithException(Func0<T, ? extends E> func, Supplier<? extends X> supplier) throws X {
        Objects.requireNonNull(func);
        Objects.requireNonNull(supplier);

        try {
            return Objects.requireNonNull(func.get());
        } catch (Throwable e) {
            throw supplier.get();
        }
    }

}
